﻿using System;
using System.Collections.Generic;
using Foothill.Common;
using Foothill.DataAccess;
using Foothill.DataAccess.Interface;
using Foothill.Model;
using System.Linq;

namespace Foothill.Service.AdService
{
    using Exceptions;

    public class AdService : IAdService
    {
        private IAdRepo _adRepo;
        private IMiscInfoRepo _miscInfoRepo;
        
        // constructor
        public AdService(IAdRepo adRepo, IMiscInfoRepo miscInfoRepo)
        {
            _adRepo = adRepo;
            _miscInfoRepo = miscInfoRepo;
        }
        
        public int InsertNewForSaleAd(ForSaleAd newAd, out string editKey)
        {
            newAd.LastEditTime = DateTime.Now;
            newAd.TimePosted = DateTime.Now;
            newAd.StatusId = (int)AdStatusLookup.Active; //initially, the status is Active

            newAd.EditKey = ""; // will be updated shortly

            int newAdId = 0;
            
            try
            {
                // insert into database and get the id
                newAdId = _adRepo.InsertNewForSaleAd(newAd);
            }
            catch(DataAccessException exception)
            {
                throw new ServiceException("data access error when inserting a new ad", exception);
            }
            
            // generate edit key
            editKey = GenerateEditKey();
            UpdateForSaleAdEditKey(newAdId, editKey);

            return newAdId;
        }
       
        public string GenerateEditKey()
        {
            return SecurityUtility.GenerateRandomPassword( GlobalSettings.EditKeyLength );
        }

        public bool UpdateForSaleAdEditKey(int adId, string editkey)
        {
            try
            {
                return _adRepo.UpdateForSaleAdEditKey(adId, editkey);
            }
            catch (DataAccessException exception)
            {
                throw new ServiceException("data access error", exception);
            }
        }

        public IDictionary<int, string> GetOrderByOptions(string cultureCode)
        {
            try
            {
                return _miscInfoRepo.GetOrderByOptions(cultureCode);
            }
            catch (DataAccessException exception)
            {
                throw new ServiceException("data access error", exception);
            }
        }

        public ForSaleAd GetForSaleAdById(int id)
        {
            if (id > 0)
            {
                try
                {
                    return _adRepo.GetForSaleAdById(id);
                }
                catch (DataAccessException exception)
                {
                    throw new ServiceException("data access error", exception);
                }
            }

            return null;
        }

        public ForSaleAd GetForSaleAdByIdAndEditKey(int postId, string editkey)
        {
            try
            {
                return _adRepo.GetForSaleAdByIdAndEditKey(postId, editkey);
            }
            catch (DataAccessException exception)
            {
                throw new ServiceException("data access error", exception);
            }
        }

        public bool UpdatePrice(int postId, int newPrice)
        {
            try
            {
                return _adRepo.UpdatePrice(postId, newPrice);
            }
            catch (DataAccessException exception)
            {
                throw new ServiceException("data access error", exception);
            }
        }

        public bool TakePostOffline(int postId)
        {
            try
            {
                return _adRepo.SetPostStatusId(postId, AdStatusLookup.Withdrawn);
            }
            catch (DataAccessException exception)
            {
                throw new ServiceException("data access error", exception);
            }
        }

        public AdStatusLookup DetermineAdStatus(ForSaleAd post)
        {
            AdStatusLookup result = AdStatusLookup.Active;

            if (post.StatusId == (int)AdStatusLookup.Active)
            {
                if (IsExpired(post.TimePosted))
                {
                    result = AdStatusLookup.Expired;
                }
            }
            else
            {
                result = (AdStatusLookup)post.StatusId;
            }

            return result;
        }

        public IList<ForSaleAd> GetPostsByUser(int userAccountId, int pageNum, out int totalCount)
        {
            totalCount = 0;
            if (userAccountId < 1)
            {
                return new List<ForSaleAd>();
            }

            IList<ForSaleAd> list = _adRepo.GetPostsByUser(userAccountId).OrderByDescending(p => p.TimePosted).ToList();
            totalCount = list.Count;
            int pageSize = GlobalSettings.PostsByUserPageSize;
            
            return list.Skip((pageNum - 1) * pageSize).Take(pageSize).ToList();
        }

        public void RemovePostFromSavedList(int userId, int postId)
        {
            if (userId < 1 || postId < 1)
            {
                return;
            }

            _adRepo.RemovePostFromSavedList(userId, postId);
        }

        public IList<ForSaleAd> GetUserSavedPosts(int userAccountId)
        {
            if (userAccountId < 1)
            {
                return new List<ForSaleAd>();
            }

            return _adRepo.GetUserSavedPosts(userAccountId);
        }

        public IList<ForSaleAd> GetUserSavedPosts(int userAccountId, int pageNum, out int totalCount)
        {
            if (userAccountId < 1)
            {
                totalCount = 0;
                return new List<ForSaleAd>();
            }

            IList<ForSaleAd> list = _adRepo.GetUserSavedPosts(userAccountId).OrderByDescending(p => p.TimePosted).ToList(); ;

            totalCount = list.Count;
            int pageSize = GlobalSettings.UserSavedListPageSize;
            return list.Skip((pageNum - 1) * pageSize).Take(pageSize).ToList();
        }

        public void IncreaseNumberOfTimeClicked(int postId)
        {
            _adRepo.IncreaseNumberOfTimeClicked(postId);
        }

        public IList<int> GetIdListForAdPostedByUser(int userAccountId)
        {
            return _adRepo.GetIdListForAdPostedByUser(userAccountId);
        }

        private bool IsExpired(DateTime datePosted)
        {
            TimeSpan diff = DateTime.Now.Subtract(datePosted);
            return diff.Days > GlobalSettings.NumberOfDaysActive;
        }
    }
}
